/*--------------------------------------------- Noble Steed Engine--------------------------------------*
Authors : Daniel Randle, Alex Bourne
Date Created : Mar 13 2013

File:
	nsmaparea.cpp

Description:
	This file contains the definition for the NSMapArea class and any associated functions that help
	the ol' girl out
*-----------------------------------------------------------------------------------------------------*/

#include <nsmaparea.h>
#include <nsobject.h>
#include <nshextile.h>
#include <nslight.h>
#include <nsobjectreference.h>
#include <nsobjectreferenceset.h>
#include <nsshaderset.h>
#include <nscamera.h>
#include <sstream>
#include <nsselectionmodel.h>
#include <nsengine.h>

#ifdef DEBUG
#include <debug.h>
#endif

/*---------------------------*
|   class NSMapArea BEGINS  |
*---------------------------*/


NSMapArea::NSMapArea(NSEngine * eng, MapSize size): engine(eng), mapSize(size), selModel(new NSSelectionModel(this)), directionalLight(NULL)
{
	LogFile("Creating MapArea...");
	for (int i = 0; i <= NSObject::SpotLight; ++i)
		referenceSets.push_back(new NSObjectReferenceSet(this));
	tileGrid = new NSTileGrid(mapSize);
}

NSMapArea::~NSMapArea()
{
	LogFile("Deleting MapArea reference sets...");
	while (referenceSets.begin() != referenceSets.end())
	{
		delete referenceSets.back();
		referenceSets.pop_back();
	}
	delete selModel;
	delete tileGrid;
	LogFile("MapArea Deleted");
}

void NSMapArea::addReference(NSObjectReference* toAdd)
{
	NSObject::ObjectType which = toAdd->getBase()->getObjectType();
	referenceSets[which]->addReference(toAdd);
	tileGrid->addReference(toAdd);
	toAdd->setProjectionMat(engine->getProjectionMatrix());
}

NSObjectReference * NSMapArea::addReference(NSObject * baseObj, const NSVec3Df & position, const NSVec3Df & orientation, const NSVec3Df & scale, NSShaderSet * shaders)
{
	NSObjectReference * ref = createReference(baseObj,position,orientation,scale,shaders);
	addReference(ref);
	return ref;
}

void NSMapArea::clear()
{
	LogFile("Deleting MapArea reference sets...");
	while (referenceSets.begin() != referenceSets.end())
	{
		delete referenceSets.back();
		referenceSets.pop_back();
	}
	delete selModel;
	LogFile("MapArea Deleted");

	selModel = new NSSelectionModel(this);

	LogFile("Creating MapArea...");
	for (int i = 0; i <= NSObject::SpotLight; ++i)
		referenceSets.push_back(new NSObjectReferenceSet(this));
}

NSObjectReference * NSMapArea::createReference(NSObject * baseObj, const NSVec3Df & position, const NSVec3Df & orientation, const NSVec3Df & scale, NSShaderSet * shaders)
{
	NSObjectReference * ref = NULL;
	NSObject::ObjectType which = baseObj->getObjectType();
	switch (which)
	{
	case (NSObject::HexTile):
		ref = new NSObjectReference(baseObj, position, orientation, scale);
		break;
	case (NSObject::Base):
		ref = new NSObjectReference(baseObj, position, orientation, scale);
		break;
	case (NSObject::PointLight):
		ref = new NSPointLightReference((NSPointLight*)baseObj,shaders, position,orientation,NSVec3Df(.2,.2,.2));
		break;
	case (NSObject::SpotLight):
		ref = new NSSpotLightReference((NSSpotLight*)baseObj,shaders, position,orientation,NSVec3Df(.2,.2,.2));
		break;
	case (NSObject::DirectionalLight):
		setDirectionalLight((NSDirectionLight*)baseObj);
		return NULL;
		break;
	}
	return ref;
}

NSObjectReference * NSMapArea::addReference(const std::string & objNm, const NSVec3Df & position, const NSVec3Df & orientation, const NSVec3Df & scale, NSShaderSet * shaders)
{
	return addReference(engine->getObject(objNm),position,orientation,scale,shaders);
}

void NSMapArea::deserialize(std::ifstream & in)
{
	Iterator iter = begin();
	while (iter != end())
	{
		unsigned int objCount = 0;
		in.read((char*)&objCount,sizeof(unsigned int));

		for (int i = 0; i < objCount; ++i)
		{
			unsigned int nmSize = 0;
			in.read((char*)&nmSize,sizeof(unsigned int));

			char * nm = new char[nmSize];
			in.read((char*)nm,nmSize);
			std::string objName(nm);
			delete [] nm;

			unsigned int refCount = 0;
			in.read((char*)&refCount,sizeof(unsigned int));

			for (int j = 0; j < refCount; ++j)
			{
				NSObjectReference* ref = engine->addReference(objName);
				ref->deserialize(in);
			}
		}
		++iter;
	}
}

void NSMapArea::drawOverlays(const NSMatrix4Df & projCamMat)
{
	selModel->draw(projCamMat);
	if (engine->getShowOccupiedTiles())
		tileGrid->drawOccupiedGridSpaces(projCamMat);
}


NSDirectionLight * NSMapArea::getDirectionalLight()
{
	return directionalLight;
}

NSEngine * NSMapArea::getEngine()
{
	return engine;
}

NSMapArea::MapSize NSMapArea::getMapSize()
{
	return mapSize;
}

NSObjectReference * NSMapArea::getReference(unsigned int refID, NSObject * baseObj)
{
	NSObjectReference * ret = NULL;
	Iterator iter = begin();
	while (iter != end())
	{
		ret = (*iter)->getReference(refID,baseObj);
		if (ret != NULL)
			return ret;
		++iter;
	}

	// Check building tool for reference id if object is translucent..
	// Translucent object references are drawn automatically when created because
	// translucent objects are not instance drawn with the instance transform vector..
	// they are only instance drawn using the picking shader - this means that
	// we must check explicitly the build tool for translucent objects
	if (baseObj->isHasTransluscentMaterial())
	{
		NSMapBuilder * mb = engine->getMapBuilder();
		if (mb->getObject() == baseObj)
		{
			ret = engine->getMapBuilder()->getReference(refID);
			if (ret != NULL)
				return ret;
		}
	}
	return ret;
}

unsigned int NSMapArea::getReferenceCount(NSObject * baseObj)
{
	unsigned int count = 0;
	Iterator iter = begin();
	while (iter != end())
	{
		count += (*iter)->getReferenceCount(baseObj);
		++iter;
	}
	return count;
}

NSSelectionModel * NSMapArea::getSelectionModel()
{
	return selModel;
}

unsigned int NSMapArea::getTotalReferenceCount()
{
	unsigned int count = 0;
	Iterator iter = begin();
	while (iter != end())
	{
		count += (*iter)->getTotalReferenceCount();
		++iter;
	}
	return count;
}

bool NSMapArea::removeAllReferencesOf(NSObject * baseObj)
{
	bool ret = false;
	Iterator iter = begin();
	while (iter != end())
	{
		ret = (*iter)->removeAllReferencesOf(baseObj);
		++iter;
	}
	return ret;
}

bool NSMapArea::removeReference(unsigned int refID, NSObject * baseObj)
{
	bool ret = false;
	Iterator iter = begin();
	while (iter != end())
	{
		ret = (*iter)->removeReference(refID,baseObj);
		++iter;
	}
	return ret;
}

bool NSMapArea::removeReference(NSObjectReference * reference)
{
	bool ret = false;
	Iterator iter = begin();
	while (iter != end())
	{
		ret = (*iter)->removeReference(reference);
		++iter;
	}
	return ret;
}

void NSMapArea::resizeScreen(int _width, int _height)
{
	screenW = _width;
	screenH = _height;
}

void NSMapArea::serialize(std::ofstream & out)
{
	Iterator iter = begin();
	while (iter != end())
	{
		unsigned int objCount = (*iter)->getObjectCount();
		out.write((char*)&objCount,sizeof(unsigned int));

		ObjectIterator iter2 = (*iter)->begin();
		while (iter2 != (*iter)->end())
		{
			std::string objName = iter2->first->getName();
			unsigned int nmSize = objName.size()+1;

			out.write((char*)&nmSize,sizeof(unsigned int));
			out.write(objName.c_str(),nmSize);

			unsigned int refCount = iter2->second.size();
			out.write((char*)&refCount,sizeof(unsigned int));

			RefIterator iter3 = iter2->second.begin();
			while (iter3 != iter2->second.end())
			{
				(*iter3)->serialize(out);
				++iter3;
			}
			++iter2;
		}
		++iter;
	}
}

void NSMapArea::setDirectionalLight(NSDirectionLight * dLight)
{
	directionalLight = dLight;
}

void NSMapArea::setProjectionMatrix(const NSMatrix4Df & proj)
{
	Iterator iter = begin();
	while (iter != end())
	{
		(*iter)->setRefsProjMat(proj);
		++iter;
	}
}

void NSMapArea::update(NSObject * baseObj)
{
	Iterator iter = begin();
	while (iter != end())
	{
		(*iter)->update(baseObj);
		++iter;
	}
}

void NSMapArea::updateAll()
{
	Iterator iter = begin();
	while (iter != end())
	{
		(*iter)->updateAll();
		++iter;
	}
}

int NSMapArea::getScreenHeight()
{
	return screenH;
}

int NSMapArea::getScreenWidth()
{
	return screenW;
}

NSMapArea::Iterator  NSMapArea::begin()
{
	return referenceSets.begin();
}

NSMapArea::Iterator  NSMapArea::end()
{
	return referenceSets.end();
}

NSMapArea::ObjectIterator  NSMapArea::objectsBegin(NSObject::ObjectType which)
{
	return referenceSets[which]->begin();
}

NSMapArea::ObjectIterator  NSMapArea::objectsEnd(NSObject::ObjectType which)
{
	return referenceSets[which]->end();
}

NSMapArea::RefIterator  NSMapArea::refsBegin(NSObject * baseObj)
{
	NSObject::ObjectType which = baseObj->getObjectType();
	return referenceSets[which]->refBegin(baseObj);
}

NSMapArea::RefIterator  NSMapArea::refsEnd(NSObject * baseObj)
{
	NSObject::ObjectType which = baseObj->getObjectType();
	return referenceSets[which]->refEnd(baseObj);
}